import os
import sys
import time
import datetime
import tempfile
import subprocess
from typing import Optional, Tuple
from PyQt5.QtCore import QThread, pyqtSignal, QEvent

# 导入配置和工具
from config.constants import (
    TASK_EVENT_TYPE, DEFAULT_RECORD_FPS, TASK_TIMEOUT,
    MIN_RECORD_SIZE, RECORD_MAX_DURATION
)
from utils.helper import logger, get_recording_path
from db.operations import (
    get_script_path_by_task_id, update_task_run_time,
    add_task_log, update_task_log
)
from ui.ffpyplayer_wrapper import ScreenRecorder  # 导入新的ScreenRecorder类



# -------------------------- 自定义事件（跨线程通信） --------------------------
class TaskEvent(QEvent):
    """定时任务触发事件（用于主线程处理定时任务）"""
    def __init__(self, task_id: int, script_path: str):
        super().__init__(TASK_EVENT_TYPE)
        self.task_id = task_id
        self.script_path = script_path


# -------------------------- 录屏线程 --------------------------
# 移除本地定义的ScreenRecorder类，直接使用从ffpyplayer_wrapper导入的类
# class ScreenRecorder(QThread):
#     """录屏线程（独立线程，避免阻塞任务执行）"""
#     # 信号：录制完成（返回文件路径，空串表示失败）
#     finished = pyqtSignal(str)
#     # 信号：错误信息
#     error = pyqtSignal(str)
#     # 信号：录制进度（已录制秒数）
#     progress = pyqtSignal(int)

#     def __init__(self, output_path: str):
#         super().__init__()
#         self.running = False
#         self.output_path = output_path  # 录屏输出路径
#         self.fps = DEFAULT_RECORD_FPS    # 帧率
#         self.start_time = None          # 开始录制时间（用于计算时长）

#     def run(self):
#         """录屏主逻辑"""
#         try:
#             # 延迟导入（避免启动时加载过多依赖）
#             import pyautogui
#             import cv2
#             import numpy as np
#             from PIL import ImageGrab

#             # 初始化参数
#             self.running = True
#             self.start_time = datetime.datetime.now()
#             pyautogui.FAILSAFE = False  # 禁用鼠标角落退出

#             # 获取屏幕分辨率
#             screen_size = pyautogui.size()
#             if screen_size[0] == 0 or screen_size[1] == 0:
#                 raise ValueError("无法获取屏幕分辨率（可能是多屏配置问题）")

#             # 初始化视频写入器（MP4格式）
#             fourcc = cv2.VideoWriter_fourcc(*"mp4v")
#             out = cv2.VideoWriter(
#                 self.output_path, fourcc, self.fps,
#                 (screen_size[0], screen_size[1])
#             )

#             if not out.isOpened():
#                 raise RuntimeError(f"无法创建视频文件：{self.output_path}（权限不足或路径无效）")

#             logger.info(f"开始录屏：路径={self.output_path}，分辨率={screen_size}，帧率={self.fps}")

#             # 录屏循环
#             while self.running:
#                 # 1. 检查是否超过最大录屏时长
#                 elapsed = (datetime.datetime.now() - self.start_time).total_seconds()
#                 self.progress.emit(int(elapsed))  # 发送进度信号
#                 if elapsed > RECORD_MAX_DURATION:
#                     self.error.emit(f"录屏时长超过最大值（{RECORD_MAX_DURATION//3600}小时），自动停止")
#                     break

#                 # 2. 捕获屏幕（支持多屏）
#                 img = ImageGrab.grab(bbox=None, all_screens=True)  # 捕获所有屏幕
#                 frame = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)  # 转换颜色空间（RGB→BGR）

#                 # 3. 写入视频帧
#                 out.write(frame)

#                 # 4. 控制帧率（避免CPU占用过高）
#                 time.sleep(1 / self.fps)

#             # 3. 录制结束：释放资源
#             out.release()

#             # 4. 检查录屏文件有效性
#             if os.path.exists(self.output_path) and os.path.getsize(self.output_path) >= MIN_RECORD_SIZE:
#                 logger.info(f"录屏完成：文件大小={os.path.getsize(self.output_path)//1024}KB，路径={self.output_path}")
#                 self.finished.emit(self.output_path)
#             else:
#                 # 删除无效文件
#                 if os.path.exists(self.output_path):
#                     os.remove(self.output_path)
#                 err_msg = f"录屏文件无效（大小<{MIN_RECORD_SIZE}字节），已删除"
#                 self.error.emit(err_msg)
#                 self.finished.emit("")

#         except Exception as e:
#             err_msg = f"录屏异常：{str(e)}"
#             logger.error(err_msg)
#             self.error.emit(err_msg)
#             # 清理无效文件
#             if os.path.exists(self.output_path):
#                 os.remove(self.output_path)
#             self.finished.emit("")

#     def stop(self):
#         """停止录屏（安全退出）"""
#         self.running = False
#         # 等待线程退出（最多2秒，确保资源释放）
#         if self.wait(2000):
#             logger.info(f"录屏线程安全停止：{self.output_path}")
#         else:
#             logger.warning(f"录屏线程等待超时，强制终止：{self.output_path}")
#             self.terminate()
#             # 清理无效文件
#             if os.path.exists(self.output_path) and os.path.getsize(self.output_path) < MIN_RECORD_SIZE:
#                 os.remove(self.output_path)


# -------------------------- 任务执行线程 --------------------------
class TaskExecutor(QThread):
    """任务执行线程（独立线程，执行Python脚本并记录日志）"""
    # 信号：日志更新（实时输出执行日志）
    log_updated = pyqtSignal(str)
    # 信号：状态更新（运行中/成功/失败/已终止）
    status_updated = pyqtSignal(str)
    # 信号：执行完成（task_id, 最终状态, 录屏路径）
    finished = pyqtSignal(int, str, str)
    # 信号：录屏进度（已录制秒数）
    record_progress = pyqtSignal(int)

    def __init__(self, task_id: int, record_screen: bool = True):
        super().__init__()
        self.task_id = task_id                # 任务ID
        self.record_screen = record_screen    # 是否录屏
        self.script_path: Optional[str] = None# 脚本路径
        self.recorder: Optional[ScreenRecorder] = None  # 录屏实例
        self.log_id: Optional[int] = None     # 日志ID
        self.start_time: str = ""             # 开始时间（字符串格式）
        self.temp_log_path: str = tempfile.mktemp(suffix='.log')  # 临时日志文件

    def run(self):
        """任务执行主逻辑"""
        try:
            # 1. 初始化任务信息
            self.start_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.script_path = get_script_path_by_task_id(self.task_id)
            task_name = f"任务ID={self.task_id}"

            # 2. 检查脚本路径有效性
            if not self.script_path or not os.path.exists(self.script_path):
                err_msg = f"{task_name} - 脚本不存在：{self.script_path}"
                self.log_updated.emit(f"❌ {err_msg}")
                self.status_updated.emit("失败")
                self._finish_task("失败", "", err_msg)
                return

            # 3. 添加任务日志（开始记录）
            log_ok, self.log_id = add_task_log(self.task_id, self.start_time)
            if not log_ok:
                self.log_updated.emit(f"⚠️ {task_name} - 日志记录失败，仍将继续执行")

            # 4. 启动录屏（如果启用）
            recording_path = ""
            if self.record_screen:
                recording_path = get_recording_path(self.task_id, self.start_time)
                self.recorder = ScreenRecorder(recording_path)
                # 连接录屏信号
                self.recorder.finished.connect(lambda path: self.log_updated.emit(f"📹 录屏文件：{path}"))
                self.recorder.error.connect(lambda err: self.log_updated.emit(f"⚠️ 录屏错误：{err}"))
                self.recorder.progress.connect(self.record_progress)  # 转发录屏进度
                self.recorder.start()
                self.log_updated.emit(f"📹 已启动录屏，文件将保存至：{recording_path}")

            # 5. 输出任务基本信息
            self.log_updated.emit(f"=== {task_name} 开始执行 ===")
            self.log_updated.emit(f"📅 开始时间：{self.start_time}")
            self.log_updated.emit(f"📜 脚本路径：{self.script_path}")
            self.log_updated.emit(f"🐍 Python解释器：{sys.executable}")
            self.log_updated.emit(f"🔍 工作目录：{os.path.dirname(self.script_path)}")
            self.status_updated.emit("运行中")

            # 6. 执行Python脚本
            self._execute_script()

        except Exception as e:
            err_msg = f"任务执行异常：{str(e)}"
            logger.error(err_msg)
            self.log_updated.emit(f"❌ {err_msg}")
            self.status_updated.emit("失败")
            self._finish_task("失败", "")

    def _execute_script(self):
        """执行Python脚本（核心逻辑）"""
        try:
            # 执行脚本（捕获stdout和stderr，超时控制）
            result = subprocess.run(
                [sys.executable, self.script_path],
                cwd=os.path.dirname(self.script_path),  # 切换到脚本所在目录
                capture_output=True,
                text=True,
                timeout=TASK_TIMEOUT,  # 超时时间
                encoding='utf-8'       # 编码
            )

            # 写入临时日志文件（完整输出）
            with open(self.temp_log_path, 'w', encoding='utf-8') as f:
                f.write(f"=== 任务执行日志 ===\n")
                f.write(f"任务ID：{self.task_id}\n")
                f.write(f"开始时间：{self.start_time}\n")
                f.write(f"Python解释器：{sys.executable}\n")
                f.write(f"脚本路径：{self.script_path}\n")
                f.write(f"返回代码：{result.returncode}\n\n")
                f.write(f"=== 标准输出（STDOUT） ===\n")
                f.write(result.stdout or "无输出\n\n")
                f.write(f"=== 标准错误（STDERR） ===\n")
                f.write(result.stderr or "无错误\n")

            # 判断执行结果
            if result.returncode == 0:
                self.log_updated.emit("✅ 脚本执行成功（返回码：0）")
                self.status_updated.emit("成功")
                self._finish_task("成功", self._get_recording_path())
            else:
                # 输出错误预览（前500字符）
                err_preview = result.stderr[:500] + "..." if len(result.stderr) > 500 else result.stderr
                self.log_updated.emit(f"❌ 脚本执行失败（返回码：{result.returncode}）")
                self.log_updated.emit(f"错误预览：{err_preview}")
                self.status_updated.emit("失败")
                self._finish_task("失败", self._get_recording_path())

        except subprocess.TimeoutExpired:
            err_msg = f"脚本执行超时（超过{TASK_TIMEOUT//60}分钟）"
            self.log_updated.emit(f"⏱️ {err_msg}")
            self.status_updated.emit("失败")
            # 写入超时日志
            with open(self.temp_log_path, 'w', encoding='utf-8') as f:
                f.write(f"=== 任务执行日志 ===\n")
                f.write(f"任务ID：{self.task_id}\n")
                f.write(f"开始时间：{self.start_time}\n")
                f.write(f"错误：{err_msg}\n")
            self._finish_task("失败", self._get_recording_path())

        except Exception as e:
            err_msg = f"脚本执行异常：{str(e)}"
            self.log_updated.emit(f"❌ {err_msg}")
            self.status_updated.emit("失败")
            # 写入异常日志
            with open(self.temp_log_path, 'w', encoding='utf-8') as f:
                f.write(f"=== 任务执行日志 ===\n")
                f.write(f"任务ID：{self.task_id}\n")
                f.write(f"开始时间：{self.start_time}\n")
                f.write(f"错误：{err_msg}\n")
            self._finish_task("失败", self._get_recording_path())

    def _finish_task(self, final_status: str, recording_path: str = "", extra_log: str = ""):
        """任务结束处理（更新日志、停止录屏、发送完成信号）"""
        end_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        task_name = f"任务ID={self.task_id}"

        # 1. 停止录屏
        if self.recorder and self.recorder.isRunning():
            self.recorder.stop()

        # 2. 读取临时日志内容
        log_content = ""
        if os.path.exists(self.temp_log_path):
            with open(self.temp_log_path, 'r', encoding='utf-8') as f:
                log_content = f.read()
            # 添加额外日志（如脚本不存在的错误）
            if extra_log:
                log_content += f"\n=== 额外信息 ===\n{extra_log}\n"
            # 删除临时文件
            os.remove(self.temp_log_path)

        # 3. 更新数据库日志
        if self.log_id:
            update_task_log(
                self.log_id,
                end_time=end_time,
                status=final_status,
                log_content=log_content,
                screen_recording_path=recording_path
            )

        # 4. 更新任务的最后运行时间和下次运行时间
        from db.operations import get_task_by_id, calculate_next_run  # 避免循环导入
        task = get_task_by_id(self.task_id)
        if task:
            next_run = calculate_next_run(task['cron_expression'])
            update_task_run_time(
                self.task_id,
                last_run=end_time,
                next_run=next_run
            )

        # 5. 输出结束信息
        self.log_updated.emit(f"=== {task_name} 执行结束 ===")
        self.log_updated.emit(f"📅 结束时间：{end_time}")
        self.log_updated.emit(f"📊 最终状态：{final_status}")
        if recording_path:
            self.log_updated.emit(f"📹 录屏文件：{recording_path}")
        # 计算执行时长
        start_dt = datetime.datetime.strptime(self.start_time, '%Y-%m-%d %H:%M:%S')
        end_dt = datetime.datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
        duration = (end_dt - start_dt).total_seconds()
        self.log_updated.emit(f"⏱️ 执行时长：{duration:.2f} 秒")

        # 6. 发送完成信号
        self.finished.emit(self.task_id, final_status, recording_path)

    def _get_recording_path(self) -> str:
        """获取录屏文件路径（如果存在）"""
        if self.recorder and os.path.exists(self.recorder.output_path):
            return self.recorder.output_path
        return ""

    def stop(self):
        """强制停止任务"""
        task_name = f"任务ID={self.task_id}"
        self.log_updated.emit(f"⚠️ {task_name} 被强制终止")
        self.status_updated.emit("已终止")

        # 1. 停止录屏
        if self.recorder and self.recorder.isRunning():
            self.recorder.stop()

        # 2. 终止线程
        self.terminate()
        self.wait(1000)  # 等待线程退出

        # 3. 完成任务（标记为已终止）
        self._finish_task("已终止", self._get_recording_path(), "任务被用户强制终止")


# -------------------------- 定时任务管理器 --------------------------
class ScheduleManager(QThread):
    """定时任务管理器（基于schedule库，独立线程运行）"""
    # 信号：定时任务触发（task_id, script_path）
    task_triggered = pyqtSignal(int, str)
    # 信号：错误信息
    error = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.running = False
        self.jobs = {}  # 存储定时任务：key=task_id，value=schedule.Job
        self.schedule_module = None  # 延迟导入schedule库

    def run(self):
        """定时任务管理器主逻辑"""
        try:
            # 延迟导入schedule库（避免启动时加载）
            import schedule
            self.schedule_module = schedule
            self.running = True

            logger.info("✅ 定时任务管理器启动成功")
            self.error.emit("定时任务管理器已启动")

            # 定时任务循环（每秒检查一次）
            while self.running:
                self.schedule_module.run_pending()
                time.sleep(1)  # 降低CPU占用

            logger.info("ℹ️ 定时任务管理器已停止")
            self.error.emit("定时任务管理器已停止")

        except Exception as e:
            err_msg = f"定时任务管理器异常：{str(e)}"
            logger.error(err_msg)
            self.error.emit(err_msg)

    # 完整的add_job方法修复
    def add_job(self, task_id: int, script_path: str, cron_expr: str) -> Tuple[bool, str]:
        """
        添加定时任务
        返回：(是否成功, 提示信息)
        """
        if not self.schedule_module:
            err_msg = "定时任务管理器未初始化"
            logger.error(err_msg)
            return False, err_msg

        if not os.path.exists(script_path):
            err_msg = f"脚本不存在：{script_path}"
            logger.warning(err_msg)
            return False, err_msg

        # 1. 先移除已存在的相同任务
        if task_id in self.jobs:
            self.schedule_module.cancel_job(self.jobs[task_id])
            del self.jobs[task_id]
            logger.info(f"移除旧定时任务：task_id={task_id}")

        # 2. 解析Cron表达式（分 时 日 月 周）
        try:
            parts = cron_expr.strip().split()
            if len(parts) != 5:
                err_msg = f"Cron表达式格式错误（需5部分）：{cron_expr}"
                return False, err_msg

            minute, hour, day, month, weekday = parts
            task_name = f"task_id={task_id}"

            # 3. 定义任务触发函数
            def job():
                if os.path.exists(script_path):
                    logger.info(f"⏰ 定时任务触发：{task_name}，脚本={script_path}")
                    self.task_triggered.emit(task_id, script_path)
                else:
                    err_msg = f"定时任务触发失败：{task_name}，脚本不存在={script_path}"
                    self.error.emit(err_msg)
                    logger.error(err_msg)

            # 4. 创建包装函数处理月份检查
            def check_month_wrapper():
                if month == '*' or datetime.datetime.now().month == int(month):
                    # 检查日期
                    if day == '*' or datetime.datetime.now().day == int(day):
                        job()

            # 5. 配置定时任务（使用schedule库的正确API）
            # 由于schedule库不支持完整的cron格式，我们使用以下策略：
            # - 最小时间单位设置为分钟
            # - 然后在包装函数中检查月份和日期
            schedule_job = self.schedule_module.every()
            
            # 配置分钟
            if minute != '*':
                try:
                    m = int(minute)
                    if 0 <= m <= 59:
                        schedule_job.minute.do(check_month_wrapper)
                    else:
                        raise ValueError(f"分钟值超出范围[0-59]：{minute}")
                except ValueError as e:
                    err_msg = f"Cron分钟配置错误：{str(e)}"
                    return False, err_msg
            else:
                # 配置小时
                if hour != '*':
                    try:
                        h = int(hour)
                        if 0 <= h <= 23:
                            schedule_job.hour.at(f":{minute if minute != '*' else '00'}").do(check_month_wrapper)
                        else:
                            raise ValueError(f"小时值超出范围[0-23]：{hour}")
                    except ValueError as e:
                        err_msg = f"Cron小时配置错误：{str(e)}"
                        return False, err_msg
                else:
                    # 如果小时和分钟都是*，则每分钟执行一次，在wrapper中检查日期和月份
                    schedule_job.minute.do(check_month_wrapper)

            # 保存任务
            self.jobs[task_id] = schedule_job

            # 6. 计算下次运行时间（用于提示）
            from utils.helper import calculate_next_run  # 避免循环导入
            next_run = calculate_next_run(cron_expr)
            if next_run:
                msg = f"任务添加成功，下次运行时间：{next_run}"
            else:
                msg = "任务添加成功，无法计算下次运行时间（Cron格式不支持）"

            logger.info(f"添加定时任务成功：{task_name}，Cron={cron_expr}")
            return True, msg

        except Exception as e:
            err_msg = f"添加定时任务失败：{str(e)}"
            logger.error(err_msg)
            return False, err_msg

    def remove_job(self, task_id: int) -> bool:
        """移除指定ID的定时任务"""
        if task_id in self.jobs and self.schedule_module:
            self.schedule_module.cancel_job(self.jobs[task_id])
            del self.jobs[task_id]
            logger.info(f"移除定时任务：task_id={task_id}")
            return True
        logger.warning(f"定时任务不存在：task_id={task_id}")
        return False

    def clear_all_jobs(self) -> None:
        """清除所有定时任务"""
        if self.schedule_module:
            self.schedule_module.clear()
            self.jobs = {}
            logger.info("清除所有定时任务")

    def stop(self):
        """停止定时任务管理器"""
        self.running = False
        self.clear_all_jobs()
        self.wait(1000)  # 等待线程退出